
#  .d8888b.           888
# d88P  Y88b          888
# Y88b.               888
#  "Y888b.    .d88b.  888888 888  888 88888b.
#     "Y88b. d8P  Y8b 888    888  888 888 "88b
#       "888 88888888 888    888  888 888  888
# Y88b  d88P Y8b.     Y88b.  Y88b 888 888 d88P
#  "Y8888P"   "Y8888   "Y888  "Y88888 88888P"
#                                     888
#                                     888
#                                     888

cmake_minimum_required (VERSION 3.15)

project (libbson
   LANGUAGES C
   # Inherit the version from mongo-c-driver
   VERSION "${PROJECT_VERSION}"
   DESCRIPTION "The libbson BSON serialization library"
)

# These values are inherited from the mongo-c-driver parent. These are named as to
# match the CMake variables generated by project().
set(libbson_VERSION_PRERELEASE ${mongo-c-driver_VERSION_PRERELEASE})
set(libbson_VERSION_FULL ${mongo-c-driver_VERSION_FULL})

# Don't export symbols implicitly
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)

# A tag attached to libbson artifacts, denoting the *major* API version, used to
# potentially co-locate additional future major versions.
set (BSON_API_VERSION 1.0)

if (APPLE)
   cmake_policy (SET CMP0042 OLD)
endif ()


#  .d8888b.           888    888    d8b
# d88P  Y88b          888    888    Y8P
# Y88b.               888    888
#  "Y888b.    .d88b.  888888 888888 888 88888b.   .d88b.  .d8888b
#     "Y88b. d8P  Y8b 888    888    888 888 "88b d88P"88b 88K
#       "888 88888888 888    888    888 888  888 888  888 "Y8888b.
# Y88b  d88P Y8b.     Y88b.  Y88b.  888 888  888 Y88b 888      X88
#  "Y8888P"   "Y8888   "Y888  "Y888 888 888  888  "Y88888  88888P'
#                                                     888
#                                                Y8b d88P
#                                                 "Y88P"

# libbson-specific configuration settings. Note that we inherit all settings
# from the parent!

mongo_setting(
   BSON_OUTPUT_BASENAME "Set the output basename for the libbson library"
   TYPE STRING
   DEFAULT VALUE "bson"
)
# Control over what components are installed:
mongo_bool_setting(
   ENABLE_STATIC_LIBBSON_INSTALL "Install static libbson"
   VISIBLE_IF ENABLE_STATIC)
mongo_bool_setting(
   ENABLE_SHARED_LIBBSON_INSTALL "Install shared libbson"
   VISIBLE_IF ENABLE_SHARED)


#  .d8888b.  888                        888
# d88P  Y88b 888                        888
# 888    888 888                        888
# 888        88888b.   .d88b.   .d8888b 888  888 .d8888b
# 888        888 "88b d8P  Y8b d88P"    888 .88P 88K
# 888    888 888  888 88888888 888      888888K  "Y8888b.
# Y88b  d88P 888  888 Y8b.     Y88b.    888 "88b      X88
#  "Y8888P"  888  888  "Y8888   "Y8888P 888  888  88888P'

# Configure-time platform checks. These start as regular CMake booleans, but are
# converted to 0/1 values (with mongo_bool01) so that they can be inserted into
# bson-config.h as preprocessor values. We cannot use #cmakedefine01, as we need
# to keep compatibility with an external Autotools-generated library configuration

include (CheckFunctionExists)
include (CheckIncludeFile)
include (CheckStructHasMember)
include (CheckSymbolExists)
include (TestBigEndian)
include (InstallRequiredSystemLibraries)
include (CheckIncludeFiles)

# See https://public.kitware.com/Bug/view.php?id=15659
check_symbol_exists (snprintf stdio.h BSON_HAVE_SNPRINTF)
mongo_bool01 (BSON_HAVE_SNPRINTF BSON_HAVE_SNPRINTF)
check_struct_has_member ("struct timespec" tv_sec time.h BSON_HAVE_TIMESPEC)
mongo_bool01 (BSON_HAVE_TIMESPEC BSON_HAVE_TIMESPEC)
check_symbol_exists (gmtime_r time.h BSON_HAVE_GMTIME_R)
mongo_bool01 (BSON_HAVE_GMTIME_R BSON_HAVE_GMTIME_R)
check_function_exists (rand_r BSON_HAVE_RAND_R)
mongo_bool01 (BSON_HAVE_RAND_R BSON_HAVE_RAND_R)
check_include_file (strings.h BSON_HAVE_STRINGS_H)
mongo_bool01 (BSON_HAVE_STRINGS_H BSON_HAVE_STRINGS_H)
check_symbol_exists (strlcpy string.h BSON_HAVE_STRLCPY)
mongo_bool01 (BSON_HAVE_STRLCPY BSON_HAVE_STRLCPY)
check_include_file (stdbool.h BSON_HAVE_STDBOOL_H)
mongo_bool01 (BSON_HAVE_STDBOOL_H BSON_HAVE_STDBOOL_H)
check_symbol_exists (clock_gettime time.h BSON_HAVE_CLOCK_GETTIME)
mongo_bool01 (BSON_HAVE_CLOCK_GETTIME BSON_HAVE_CLOCK_GETTIME)
check_symbol_exists (strnlen string.h BSON_HAVE_STRNLEN)
mongo_bool01 (BSON_HAVE_STRNLEN BSON_HAVE_STRNLEN)
test_big_endian (BSON_BIG_ENDIAN)

if (WIN32)
   set (BSON_OS 2)
else ()
   set (BSON_OS 1)
endif ()

if (BSON_BIG_ENDIAN)
   set (BSON_BYTE_ORDER 4321)
else ()
   set (BSON_BYTE_ORDER 1234)
endif ()

configure_file (
   "${PROJECT_SOURCE_DIR}/src/bson/bson-config.h.in"
   "${PROJECT_BINARY_DIR}/src/bson/bson-config.h"
)

configure_file (
   "${PROJECT_SOURCE_DIR}/src/bson/bson-version.h.in"
   "${PROJECT_BINARY_DIR}/src/bson/bson-version.h"
)

if (ENABLE_APPLE_FRAMEWORK)
   configure_file (
      "${PROJECT_SOURCE_DIR}/src/bson/modules/module.modulemap.in"
      "${PROJECT_BINARY_DIR}/src/bson/modules/module.modulemap"
   )
endif ()

# 8888888b.            .d888 d8b          d8b 888    d8b
# 888  "Y88b          d88P"  Y8P          Y8P 888    Y8P
# 888    888          888                     888
# 888    888  .d88b.  888888 888 88888b.  888 888888 888  .d88b.  88888b.  .d8888b
# 888    888 d8P  Y8b 888    888 888 "88b 888 888    888 d88""88b 888 "88b 88K
# 888    888 88888888 888    888 888  888 888 888    888 888  888 888  888 "Y8888b.
# 888  .d88P Y8b.     888    888 888  888 888 Y88b.  888 Y88..88P 888  888      X88
# 8888888P"   "Y8888  888    888 888  888 888  "Y888 888  "Y88P"  888  888  88888P'

# Base INTERFACE library propagates in-build-tree requirements for using libbson
add_library(_libbson_build_interface INTERFACE)
# Header directories required for libbson in-source
target_include_directories(_libbson_build_interface INTERFACE
   # The default src/
   src/
   # The generated src/
   ${PROJECT_BINARY_DIR}/src/
   # The source directories of the common lib:
   ${mongo-c-driver_SOURCE_DIR}/src/common/
   # Generated:
   ${mongo-c-driver_BINARY_DIR}/src/common/
   )
target_link_libraries(_libbson_build_interface INTERFACE mongo::detail::c_platform)

# Collect all source files
file(GLOB_RECURSE all_sources CONFIGURE_DEPENDS
   src/*.c src/*.h
   # Include common library source files within libbson itself (names
   # will be mangled with MCOMMON_NAME_PREFIX)
   "${mongo-c-driver_SOURCE_DIR}/src/common/*.c"
   )

# The default object library for all libbson translation units:
add_library(bson_obj OBJECT EXCLUDE_FROM_ALL ${all_sources})

# The libbson object libraries that we will build
set(bson_obj_libs bson_obj)

if(ENABLE_PIC OR WIN32)
   # User wants (or platform requires) static libs to use PIC code. Since we
   # already need PIC for the dynamic library, we can consolidate things and
   # use a single object library for both the static and the shared library.
   # No duplicate compilations necessary!
   set_property(TARGET bson_obj PROPERTY POSITION_INDEPENDENT_CODE TRUE)
   # The bson_obj_pic is just an alias of the base library:
   add_library(bson_obj_pic ALIAS bson_obj)
else()
   # User does not want PIC in the static library. In that case, we just need a second object
   # library that has PIC enabled so it can be used in creating the dynamic library.
   add_library(bson_obj_pic OBJECT EXCLUDE_FROM_ALL ${all_sources})
   set_property(TARGET bson_obj_pic PROPERTY POSITION_INDEPENDENT_CODE TRUE)
   list(APPEND bson_obj_libs bson_obj_pic)
endif()

# Set target properties for the object libraries.
mongo_target_requirements(
   ${bson_obj_libs}
   LINK_LIBRARIES
      PUBLIC
         _libbson_build_interface
   COMPILE_DEFINITIONS
      PRIVATE
         # Tell headers that they are part of compilation:
         BSON_COMPILATION
         # Enable NaN parsing in jsonsl
         JSONSL_PARSE_NAN
         # Set the name mangling scheme for the common libraries
         MCOMMON_NAME_PREFIX=_bson_mcommon
   COMPILE_OPTIONS
      PRIVATE
         # Macro constant INFINITY triggers constant arithmetic overflow warnings in
         # VS 2013, but VS 2013 doesn't support inline warning suppression.
         # Remove once support for VS 2013 is dropped.
         $<$<AND:$<C_COMPILER_ID:MSVC>,$<VERSION_LESS:${MSVC_VERSION},1900>>:/wd4756>
)

# List of the primary BSON library targets that we are building
set(bson_libs)

if(ENABLE_STATIC)
   add_library(bson_static STATIC)
   target_link_libraries(bson_static PRIVATE $<BUILD_INTERFACE:bson_obj>)
   list(APPEND bson_libs bson_static)
   # When consumers link against bson_static, suppress the annotation __declspec(dllimport),
   # since those symbols will be available immediately at the link step:
   target_compile_definitions(bson_static INTERFACE BSON_STATIC)
endif()

if(ENABLE_SHARED)
   add_library(bson_shared SHARED)
   target_link_libraries(bson_shared PRIVATE $<BUILD_INTERFACE:bson_obj_pic>)
   list(APPEND bson_libs bson_shared)
endif()

if(NOT bson_libs)
   message(FATAL_ERROR "Neither bson_shared nor bson_static is going to be built. Did you mean to enable at least one of them?")
endif()

mongo_target_requirements(
   ${bson_libs} LINK_LIBRARIES PUBLIC
   # Build-local requirements:
   $<BUILD_INTERFACE:_libbson_build_interface>
   # Include in the install interface explicitly:
   mongo::detail::c_platform
)
set_target_properties(${bson_libs} PROPERTIES
   VERSION "0.0.0"
   SOVERSION "0"
   OUTPUT_NAME "${BSON_OUTPUT_BASENAME}-${BSON_API_VERSION}"
)
if(TARGET bson_static)
   set_property(TARGET bson_static PROPERTY OUTPUT_NAME "${BSON_OUTPUT_BASENAME}-static-${BSON_API_VERSION}")
endif()

if (ENABLE_APPLE_FRAMEWORK)
   set_target_properties(bson_shared PROPERTIES
      FRAMEWORK TRUE
      MACOSX_FRAMEWORK_BUNDLE_VERSION ${MONGOC_VERSION}
      MACOSX_FRAMEWORK_SHORT_VERSION_STRING ${MONGOC_VERSION}
      MACOSX_FRAMEWORK_IDENTIFIER org.mongodb.bson
      OUTPUT_NAME "${BSON_OUTPUT_BASENAME}"
      PUBLIC_HEADER "${HEADERS}"
   )
endif ()


# 8888888888                                          888
# 888                                                 888
# 888                                                 888
# 8888888    888  888  8888b.  88888b.d88b.  88888b.  888  .d88b.  .d8888b
# 888        `Y8bd8P'     "88b 888 "888 "88b 888 "88b 888 d8P  Y8b 88K
# 888          X88K   .d888888 888  888  888 888  888 888 88888888 "Y8888b.
# 888        .d8""8b. 888  888 888  888  888 888 d88P 888 Y8b.          X88
# 8888888888 888  888 "Y888888 888  888  888 88888P"  888  "Y8888   88888P'
#                                            888
#                                            888
#                                            888

function (add_example bin src)
   set (BSON_EXAMPLE_SOURCES ${PROJECT_SOURCE_DIR}/${src})
   add_executable (${bin} ${BSON_EXAMPLE_SOURCES})

   # Link against the shared lib like normal apps
   if(TARGET bson_shared)
      target_link_libraries (${bin} bson_shared)
   elseif(TARGET bson_static)
      target_link_libraries (${bin} bson_static)
   else()
      return()
   endif()
endfunction ()

if (ENABLE_EXAMPLES)
   add_example (bcon-col-view examples/bcon-col-view.c)
   add_example (bcon-speed examples/bcon-speed.c)
   add_example (bson-metrics examples/bson-metrics.c)
   if (NOT WIN32)
      target_link_libraries (bson-metrics m)
      add_example (bson-streaming-reader examples/bson-streaming-reader.c)
   endif ()
   add_example (bson-to-json examples/bson-to-json.c)
   add_example (bson-validate examples/bson-validate.c)
   add_example (json-to-bson examples/json-to-bson.c)
   add_example (bson-check-depth examples/bson-check-depth.c)
   add_example (creating examples/creating.c)
endif () # ENABLE_EXAMPLES


# 8888888                   888             888 888
#   888                     888             888 888
#   888                     888             888 888
#   888   88888b.  .d8888b  888888  8888b.  888 888
#   888   888 "88b 88K      888        "88b 888 888
#   888   888  888 "Y8888b. 888    .d888888 888 888
#   888   888  888      X88 Y88b.  888  888 888 888
# 8888888 888  888  88888P'  "Y888 "Y888888 888 888

set (BSON_HEADER_INSTALL_DIR
   "${CMAKE_INSTALL_INCLUDEDIR}/libbson-${BSON_API_VERSION}"
)
function(install_export_target target)
   # Tell pkg-config where the headers are going:
   set_property(TARGET ${target} APPEND PROPERTY pkg_config_INCLUDE_DIRECTORIES "${BSON_HEADER_INSTALL_DIR}")
   # Install the target:
   install(
      TARGETS "${target}"
      # Important: We generate a unique export set for this target alone.
      EXPORT "${target}-targets"
      LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
      ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
      RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
      INCLUDES DESTINATION "${BSON_HEADER_INSTALL_DIR}"
      FRAMEWORK DESTINATION "${CMAKE_INSTALL_BINDIR}"
   )
   # Install the unique export set into a file that is qualified by the name of
   # the target itself. The main config-file package will search for the
   # possibly-installed exported targets for the known targets. See: bson-config.cmake
   install(
      EXPORT "${target}-targets"
      NAMESPACE mongo::
      FILE "${target}-targets.cmake"
      DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bson-${BSON_API_VERSION}"
   )
endfunction()

if(ENABLE_STATIC_LIBBSON_INSTALL AND TARGET bson_static)
   install_export_target(bson_static)
   mongo_generate_pkg_config(bson_static FILENAME libbson-static-1.0.pc INSTALL)
endif()
if(ENABLE_SHARED_LIBBSON_INSTALL AND TARGET bson_shared)
   install_export_target(bson_shared)
   mongo_generate_pkg_config(bson_shared FILENAME libbson-1.0.pc INSTALL)
endif()

# Install all headers by doing a recursive directory-install.
install(
   DIRECTORY
      # Trailing "/" requests directory contents, not the dir itself:
      src/bson/
      # Also get the generated dir:
      ${PROJECT_BINARY_DIR}/src/bson/
   DESTINATION "${BSON_HEADER_INSTALL_DIR}/bson"
   # Only grab the *public* headers.
   FILES_MATCHING
      PATTERN "*.h"
      PATTERN "*-private.h" EXCLUDE
      # Installed separately below:
      PATTERN "forwarding" EXCLUDE
      # Don't generate an empty "modules" directory
      PATTERN "modules" EXCLUDE
   )

# The forwarding header is for compatibility with `#include <bson.h>`, so it goes at the top-level:
install(FILES src/bson/forwarding/bson.h DESTINATION "${BSON_HEADER_INSTALL_DIR}")

if (ENABLE_APPLE_FRAMEWORK)
   install (
      FILES "${PROJECT_BINARY_DIR}/src/bson/modules/module.modulemap"
      DESTINATION "${CMAKE_INSTALL_BINDIR}/bson.framework/Modules/"
   )
endif ()

# Generate the config-file package
include (CMakePackageConfigHelpers)
write_basic_package_version_file (
   "${CMAKE_CURRENT_BINARY_DIR}/bson/bson-${BSON_API_VERSION}-config-version.cmake"
   VERSION ${BSON_VERSION}
   COMPATIBILITY AnyNewerVersion
)

configure_file (src/bson-config.cmake
   "${CMAKE_CURRENT_BINARY_DIR}/bson/bson-${BSON_API_VERSION}-config.cmake"
   COPYONLY
)

install (
   FILES
      "${CMAKE_CURRENT_BINARY_DIR}/bson/bson-${BSON_API_VERSION}-config.cmake"
      "${CMAKE_CURRENT_BINARY_DIR}/bson/bson-${BSON_API_VERSION}-config-version.cmake"
   DESTINATION
      ${CMAKE_INSTALL_LIBDIR}/cmake/bson-${BSON_API_VERSION}
   COMPONENT
      Devel
)

# Install the base targets. (Prior targets were installed above)
install (EXPORT bson-targets
   NAMESPACE mongo::
   FILE bson-targets.cmake
   DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/bson-${BSON_API_VERSION}
)

include (LegacyPackage)
include (CPack)

# 8888888b.
# 888  "Y88b
# 888    888
# 888    888  .d88b.   .d8888b .d8888b
# 888    888 d88""88b d88P"    88K
# 888    888 888  888 888      "Y8888b.
# 888  .d88P Y88..88P Y88b.         X88
# 8888888P"   "Y88P"   "Y8888P  88888P'

if (ENABLE_MAN_PAGES OR ENABLE_HTML_DOCS)
   find_package (Sphinx REQUIRED)
   add_subdirectory (doc)
   add_custom_target (bson-doc
      ALL
      DEPENDS
      $<$<BOOL:${ENABLE_MAN_PAGES}>:bson-man>
      $<$<BOOL:${ENABLE_HTML_DOCS}>:bson-html>
   )
endif ()
